#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
IDE/编译器验证器（B层）
在所有混淆完成后调用本机开发环境进行最终验证
支持Xcode、Android Studio、编译器等
"""

import os
import subprocess
import json
import tempfile
import shutil
from typing import Dict, List, Optional, Tuple, Set
from pathlib import Path
from dataclasses import dataclass, field

from ..utils_module.language_utils import detect_language_for_file, language_to_extensions

@dataclass
class IDEValidationResult:
    """IDE验证结果"""
    is_valid: bool
    tool_name: str
    project_path: str
    errors: List[Dict] = field(default_factory=list)
    warnings: List[Dict] = field(default_factory=list)
    output: str = ""
    command: str = ""
    
    def add_error(self, message: str, file: str = "", line: int = 0):
        self.errors.append({
            'message': message,
            'file': file,
            'line': line,
            'severity': 'error'
        })
        self.is_valid = False
    
    def add_warning(self, message: str, file: str = "", line: int = 0):
        self.warnings.append({
            'message': message,
            'file': file,
            'line': line,
            'severity': 'warning'
        })


class IDEValidator:
    """IDE/编译器验证器"""
    
    def __init__(self, config, logger):
        self.config = config
        self.logger = logger
        self.available_tools = self._detect_available_tools()
    
    def _detect_available_tools(self) -> Dict[str, str]:
        """检测可用的开发工具"""
        tools = {}
        
        # iOS开发工具
        if shutil.which('xcodebuild'):
            tools['xcodebuild'] = shutil.which('xcodebuild')
            self.logger.log_operation("检测到工具", "xcodebuild")
        
        if shutil.which('swift'):
            tools['swift'] = shutil.which('swift')
            self.logger.log_operation("检测到工具", "swift")
        
        if shutil.which('swiftc'):
            tools['swiftc'] = shutil.which('swiftc')
            self.logger.log_operation("检测到工具", "swiftc")
        
        # Android开发工具
        if os.path.exists('./gradlew'):
            tools['gradle'] = './gradlew'
            self.logger.log_operation("检测到工具", "Gradle Wrapper")
        elif shutil.which('gradle'):
            tools['gradle'] = shutil.which('gradle')
            self.logger.log_operation("检测到工具", "gradle")
        
        if shutil.which('kotlinc'):
            tools['kotlinc'] = shutil.which('kotlinc')
            self.logger.log_operation("检测到工具", "kotlinc")
        
        if shutil.which('javac'):
            tools['javac'] = shutil.which('javac')
            self.logger.log_operation("检测到工具", "javac")
        
        # 通用工具
        if shutil.which('clang'):
            tools['clang'] = shutil.which('clang')
            self.logger.log_operation("检测到工具", "clang")
        
        return tools
    
    def validate_project(self, project_path: str) -> IDEValidationResult:
        """验证整个项目"""
        self.logger.log_operation("开始IDE验证", f"项目: {project_path}")
        
        # 检测项目类型
        project_type = self._detect_project_type(project_path)
        
        if project_type == 'ios':
            return self._validate_ios_project(project_path)
        elif project_type == 'android':
            return self._validate_android_project(project_path)
        elif project_type == 'mixed':
            # 混合项目，分别验证
            ios_result = self._validate_ios_project(project_path)
            android_result = self._validate_android_project(project_path)
            
            # 合并结果
            result = IDEValidationResult(
                is_valid=ios_result.is_valid and android_result.is_valid,
                tool_name="Mixed Validation",
                project_path=project_path
            )
            result.errors.extend(ios_result.errors)
            result.errors.extend(android_result.errors)
            result.warnings.extend(ios_result.warnings)
            result.warnings.extend(android_result.warnings)
            return result
        else:
            return self._validate_generic_project(project_path)
    
    def _detect_project_type(self, project_path: str) -> str:
        """检测项目类型"""
        has_xcodeproj = False
        has_gradle = False
        detected_languages: Set[str] = set()

        for root, dirs, files in os.walk(project_path):
            # 检查Xcode项目
            if any(d.endswith('.xcodeproj') or d.endswith('.xcworkspace') for d in dirs):
                has_xcodeproj = True
            
            # 检查Gradle项目
            if 'build.gradle' in files or 'build.gradle.kts' in files:
                has_gradle = True

            # 检查源文件
            for file in files:
                language = detect_language_for_file(file)
                if language:
                    detected_languages.add(language)

        has_swift = 'swift' in detected_languages
        has_kotlin = 'kotlin' in detected_languages

        if has_xcodeproj or has_swift:
            if has_gradle or has_kotlin:
                return 'mixed'
            return 'ios'
        elif has_gradle or has_kotlin:
            return 'android'
        else:
            return 'unknown'
    
    def _validate_ios_project(self, project_path: str) -> IDEValidationResult:
        """验证iOS项目"""
        result = IDEValidationResult(
            is_valid=True,
            tool_name="Xcode",
            project_path=project_path
        )
        
        # 查找.xcodeproj或.xcworkspace
        xcodeproj = None
        xcworkspace = None
        
        for item in os.listdir(project_path):
            if item.endswith('.xcworkspace'):
                xcworkspace = os.path.join(project_path, item)
                break
            elif item.endswith('.xcodeproj'):
                xcodeproj = os.path.join(project_path, item)
        
        project_file = xcworkspace or xcodeproj
        
        if not project_file:
            # 没有Xcode项目文件，尝试直接编译Swift文件
            return self._validate_swift_files(project_path)
        
        # 使用xcodebuild进行验证
        if 'xcodebuild' not in self.available_tools:
            result.add_warning("未找到xcodebuild，跳过Xcode验证")
            return result
        
        try:
            # 构建命令
            cmd = [
                self.available_tools['xcodebuild'],
                '-project' if xcodeproj else '-workspace',
                project_file,
                '-scheme', self._get_xcode_scheme(project_file),
                '-configuration', 'Debug',
                '-sdk', 'iphonesimulator',
                'clean', 'build',
                'CODE_SIGN_IDENTITY=""',
                'CODE_SIGNING_REQUIRED=NO',
                'CODE_SIGNING_ALLOWED=NO',
                '-quiet'
            ]
            
            result.command = ' '.join(cmd)
            self.logger.log_operation("执行Xcode验证", result.command)
            
            # 执行验证
            process = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=120,
                cwd=project_path
            )
            
            result.output = process.stdout + process.stderr
            
            if process.returncode != 0:
                # 解析错误信息
                self._parse_xcode_errors(process.stderr, result)
            else:
                self.logger.log_operation("Xcode验证", "编译成功")
            
        except subprocess.TimeoutExpired:
            result.add_error("Xcode编译超时（120秒）")
        except Exception as e:
            result.add_error(f"Xcode验证失败: {str(e)}")
        
        return result
    
    def _validate_android_project(self, project_path: str) -> IDEValidationResult:
        """验证Android项目"""
        result = IDEValidationResult(
            is_valid=True,
            tool_name="Gradle",
            project_path=project_path
        )
        
        # 查找Gradle文件
        gradle_wrapper = os.path.join(project_path, 'gradlew')
        gradle_file = os.path.join(project_path, 'build.gradle')
        gradle_kts_file = os.path.join(project_path, 'build.gradle.kts')
        
        if not (os.path.exists(gradle_file) or os.path.exists(gradle_kts_file)):
            # 没有Gradle项目文件，尝试直接编译Kotlin/Java文件
            return self._validate_android_files(project_path)
        
        # 使用Gradle进行验证
        gradle_cmd = None
        if os.path.exists(gradle_wrapper):
            gradle_cmd = gradle_wrapper
            if os.name == 'nt':  # Windows
                gradle_cmd = gradle_wrapper + '.bat'
        elif 'gradle' in self.available_tools:
            gradle_cmd = self.available_tools['gradle']
        
        if not gradle_cmd:
            result.add_warning("未找到Gradle，跳过Android验证")
            return result
        
        try:
            # 构建命令
            cmd = [gradle_cmd, 'compileDebugSources', '--no-daemon']
            
            result.command = ' '.join(cmd)
            self.logger.log_operation("执行Gradle验证", result.command)
            
            # 执行验证
            process = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=120,
                cwd=project_path
            )
            
            result.output = process.stdout + process.stderr
            
            if process.returncode != 0:
                # 解析错误信息
                self._parse_gradle_errors(process.stderr, result)
            else:
                self.logger.log_operation("Gradle验证", "编译成功")
            
        except subprocess.TimeoutExpired:
            result.add_error("Gradle编译超时（120秒）")
        except Exception as e:
            result.add_error(f"Gradle验证失败: {str(e)}")
        
        return result
    
    def _validate_swift_files(self, project_path: str) -> IDEValidationResult:
        """直接验证Swift文件"""
        result = IDEValidationResult(
            is_valid=True,
            tool_name="Swift Compiler",
            project_path=project_path
        )
        
        if 'swiftc' not in self.available_tools:
            result.add_warning("未找到Swift编译器")
            return result
        
        # 收集所有Swift文件
        swift_extensions = set(language_to_extensions('swift'))
        swift_files = []
        for root, dirs, files in os.walk(project_path):
            for file in files:
                if any(file.endswith(ext) for ext in swift_extensions):
                    swift_files.append(os.path.join(root, file))
        
        if not swift_files:
            result.add_warning("未找到Swift文件")
            return result
        
        # 验证每个文件
        for swift_file in swift_files:
            try:
                cmd = [self.available_tools['swiftc'], '-parse', swift_file]
                process = subprocess.run(
                    cmd,
                    capture_output=True,
                    text=True,
                    timeout=30
                )
                
                if process.returncode != 0:
                    self._parse_swift_errors(process.stderr, result, swift_file)
                    
            except subprocess.TimeoutExpired:
                result.add_error(f"编译超时: {swift_file}")
            except Exception as e:
                result.add_error(f"验证失败: {swift_file}: {str(e)}")
        
        return result
    
    def _validate_android_files(self, project_path: str) -> IDEValidationResult:
        """直接验证Android文件"""
        result = IDEValidationResult(
            is_valid=True,
            tool_name="Kotlin/Java Compiler",
            project_path=project_path
        )
        
        # 验证Kotlin文件
        if 'kotlinc' in self.available_tools:
            kotlin_extensions = set(language_to_extensions('kotlin'))
            kotlin_files = []
            for root, dirs, files in os.walk(project_path):
                for file in files:
                    if any(file.endswith(ext) for ext in kotlin_extensions):
                        kotlin_files.append(os.path.join(root, file))
            
            if kotlin_files:
                self._validate_kotlin_files(kotlin_files, result)
        
        # 验证Java文件
        if 'javac' in self.available_tools:
            java_extensions = set(language_to_extensions('java'))
            java_files = []
            for root, dirs, files in os.walk(project_path):
                for file in files:
                    if any(file.endswith(ext) for ext in java_extensions):
                        java_files.append(os.path.join(root, file))
            
            if java_files:
                self._validate_java_files(java_files, result)
        
        return result
    
    def _validate_kotlin_files(self, files: List[str], result: IDEValidationResult):
        """验证Kotlin文件"""
        for kt_file in files:
            try:
                with tempfile.TemporaryDirectory() as temp_dir:
                    cmd = [
                        self.available_tools['kotlinc'],
                        '-no-stdlib',
                        kt_file,
                        '-d', temp_dir
                    ]
                    
                    process = subprocess.run(
                        cmd,
                        capture_output=True,
                        text=True,
                        timeout=30
                    )
                    
                    if process.returncode != 0:
                        self._parse_kotlin_errors(process.stderr, result, kt_file)
                        
            except subprocess.TimeoutExpired:
                result.add_error(f"Kotlin编译超时: {kt_file}")
            except Exception as e:
                result.add_error(f"Kotlin验证失败: {kt_file}: {str(e)}")
    
    def _validate_java_files(self, files: List[str], result: IDEValidationResult):
        """验证Java文件"""
        for java_file in files:
            try:
                cmd = [self.available_tools['javac'], '-Xlint:all', java_file]
                
                process = subprocess.run(
                    cmd,
                    capture_output=True,
                    text=True,
                    timeout=30
                )
                
                if process.returncode != 0:
                    self._parse_java_errors(process.stderr, result, java_file)
                    
            except subprocess.TimeoutExpired:
                result.add_error(f"Java编译超时: {java_file}")
            except Exception as e:
                result.add_error(f"Java验证失败: {java_file}: {str(e)}")
    
    def _validate_generic_project(self, project_path: str) -> IDEValidationResult:
        """验证通用项目"""
        result = IDEValidationResult(
            is_valid=True,
            tool_name="Generic Validator",
            project_path=project_path
        )
        
        # 尝试各种编译器
        self._validate_swift_files(project_path)
        self._validate_android_files(project_path)
        
        return result
    
    def _get_xcode_scheme(self, project_file: str) -> str:
        """获取Xcode scheme"""
        # 尝试获取第一个可用的scheme
        try:
            cmd = [
                self.available_tools['xcodebuild'],
                '-list',
                '-project' if project_file.endswith('.xcodeproj') else '-workspace',
                project_file
            ]
            
            process = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=10
            )
            
            if process.returncode == 0:
                # 解析输出获取scheme
                lines = process.stdout.split('\n')
                in_schemes = False
                for line in lines:
                    if 'Schemes:' in line:
                        in_schemes = True
                        continue
                    if in_schemes and line.strip():
                        return line.strip()
            
        except Exception:
            pass
        
        # 默认返回项目名称作为scheme
        return Path(project_file).stem
    
    def _parse_xcode_errors(self, stderr: str, result: IDEValidationResult):
        """解析Xcode错误信息"""
        for line in stderr.split('\n'):
            if 'error:' in line.lower():
                # 提取文件名和错误信息
                if ':' in line:
                    parts = line.split(':')
                    if len(parts) >= 4:
                        file_path = parts[0]
                        try:
                            line_num = int(parts[1])
                        except:
                            line_num = 0
                        message = ':'.join(parts[3:]).strip()
                        result.add_error(message, file_path, line_num)
                    else:
                        result.add_error(line.strip())
                else:
                    result.add_error(line.strip())
            elif 'warning:' in line.lower():
                result.add_warning(line.strip())
    
    def _parse_gradle_errors(self, stderr: str, result: IDEValidationResult):
        """解析Gradle错误信息"""
        for line in stderr.split('\n'):
            if 'error:' in line.lower() or 'ERROR' in line:
                # 提取错误信息
                if '.kt:' in line or '.java:' in line:
                    # Kotlin/Java编译错误
                    parts = line.split(':')
                    if len(parts) >= 3:
                        file_path = parts[0]
                        try:
                            line_num = int(parts[1])
                        except:
                            line_num = 0
                        message = ':'.join(parts[2:]).strip()
                        result.add_error(message, file_path, line_num)
                else:
                    result.add_error(line.strip())
            elif 'warning:' in line.lower() or 'WARNING' in line:
                result.add_warning(line.strip())
    
    def _parse_swift_errors(self, stderr: str, result: IDEValidationResult, file_path: str = ""):
        """解析Swift错误信息"""
        for line in stderr.split('\n'):
            if ':' in line and ('error:' in line or 'warning:' in line):
                parts = line.split(':')
                if len(parts) >= 4:
                    try:
                        line_num = int(parts[1])
                        message = ':'.join(parts[3:]).strip()
                        if 'error:' in line:
                            result.add_error(message, file_path, line_num)
                        else:
                            result.add_warning(message, file_path, line_num)
                    except:
                        result.add_error(line.strip(), file_path)
    
    def _parse_kotlin_errors(self, stderr: str, result: IDEValidationResult, file_path: str = ""):
        """解析Kotlin错误信息"""
        for line in stderr.split('\n'):
            if '.kt:' in line and ('error:' in line or 'warning:' in line):
                try:
                    parts = line.split(':')
                    if len(parts) >= 3:
                        line_num = int(parts[1])
                        message = ':'.join(parts[2:]).strip()
                        if 'error:' in line:
                            result.add_error(message, file_path, line_num)
                        else:
                            result.add_warning(message, file_path, line_num)
                except:
                    result.add_error(line.strip(), file_path)
    
    def _parse_java_errors(self, stderr: str, result: IDEValidationResult, file_path: str = ""):
        """解析Java错误信息"""
        for line in stderr.split('\n'):
            if '.java:' in line and 'error:' in line:
                try:
                    parts = line.split(':')
                    if len(parts) >= 3:
                        line_num = int(parts[1])
                        message = ':'.join(parts[2:]).strip()
                        result.add_error(message, file_path, line_num)
                except:
                    result.add_error(line.strip(), file_path)
            elif 'warning:' in line:
                result.add_warning(line.strip(), file_path)
    
    def save_validation_report(self, result: IDEValidationResult, output_path: str):
        """保存验证报告"""
        report = {
            'is_valid': result.is_valid,
            'tool': result.tool_name,
            'project': result.project_path,
            'command': result.command,
            'errors': result.errors,
            'warnings': result.warnings,
            'error_count': len(result.errors),
            'warning_count': len(result.warnings)
        }
        
        try:
            with open(output_path, 'w', encoding='utf-8') as f:
                json.dump(report, f, indent=2, ensure_ascii=False)
            self.logger.log_operation("IDE验证报告", f"已保存到: {output_path}")
        except Exception as e:
            self.logger.log_error(f"保存IDE验证报告失败: {e}")
